#ifndef __CM_NAT_STUN_AGENT_H__
#define __CM_NAT_STUN_AGENT_H__

#include "Common.h"
#include "BaseTimer.h"
#include "StunMessage.h"
#include "BaseNotification.h"
#include "ByteBufferPool.h"

#include "Poco/Random.h"
#include "Poco/Mutex.h"
#include "Poco/Net/SocketAddress.h"

using Poco::FastMutex;
using Poco::Net::SocketAddress;

namespace cm_nat
{
    class StunAgent;

    class StunAgentListener
    {
    public:
        //send out StunAgent's request/response message data
        virtual void onToSend(StunAgent* p_agent, ByteBuffer* p_data,const SocketAddress& sa) =0;

        virtual void onCheckResult(StunAgent* p_agent, int result) = 0;
        
        enum
        {
            CHECK_RES_CHECK_FAIL,   //connect fail
            CHECK_RES_CHECK_SUCCESS,//connection setup
            CHECK_RES_REFLEX_UPDATE,  //reflex address updated
            CHECK_RES_KEEP_FAIL
        };
    };

    class StunAgent : public BaseTimerListener
    {
    public:

        StunAgent( ByteBufferPool* p_pool, BaseTimer* p_timer );
        ~StunAgent();

        int setup();
        int teardown();

        void registerListener(StunAgentListener* p_listener);

        //start to check connection with remote
        int startCheck( SocketAddress remote );
        //keep alive with remote
        int keepCheck();
        //stop checking
        int stopCheck();

        SocketAddress getReflexAddress();

        //filter & eat received data
        bool filterStunPacket( ByteBuffer* data );

        //from BaseTimerListener
        void onTimer(TimerNotification* p_tn);

        static bool detectMessage(ByteBuffer* data);

        int processMessage( ByteBuffer* data );

    protected:
        int sendRequest(SocketAddress remote, std::string& tid);

        int processResponse( StunMessage* p_sm,const SocketAddress& from  );
        int processErrorResponse( StunMessage* p_sm, const SocketAddress& from );
        int processRequest( StunMessage* p_sm, const SocketAddress& from );

        int sendTo(ByteBuffer* data,const SocketAddress& sa);

        std::string generateTid();

        void startTimer(long interval, UInt32 context);

        enum
        {
            TIMER_START_CHECK               = 0x0001,
            TIMER_KEEP_CHECK                = 0x0002,
        };

    protected:
        ByteBufferPool* m_bufferPool;
        BaseTimer* m_timer;

        StunAgentListener* m_listener;
        Poco::Random m_random;

        SocketAddress m_reflexAddress;
        SocketAddress m_remoteAddress;

        std::string m_lastTid;

        bool m_keepCheckFlag;
        bool m_connectFlag; 
        bool m_checkFailFlag;

        UInt16 m_checkFailCount;

        FastMutex m_mutex;
    };

#define KEEP_CHECK_FAIL_MAX 3
}

#endif